COSA.Util = {};
(function ( exports ) {

    //region StringBuilder

    function StringBuilder() {
        var _str = [];

        var append = function (str) {
            _str.push(str);
        };

        var concatAndAppend = function () {
            for (var i = 0, len = arguments.length; i < len; i++) {
                _str.push(arguments[i]);
            }
        };

        var toString = function () {
            return _str.join('');
        };

        var clear = function () {
            _str = [];
        };

        return {
            append: append,
            toString: toString,
            clear: clear,
            concatAndAppend: concatAndAppend
        };
    }

    //endregion StringBuilder

    //region StrBuilder

    function StrBuilder () {
        this.str = [];
    }

    StrBuilder.prototype = {
        append: function ( str ) {
            this.str.push(str);
            return this;
        },
        cappend: function () {
            for ( var i = 0, len = arguments.length; i < len; i++ ) {
                this.str.push(arguments[i]);
            }
            return this;
        },
        toString: function () {
            return this.str.join('');
        },
        clear: function () {
            this.str = [];
            return this;
        }
    };

    //endregion StrBuilder

    //region StringFormat

    function StringFormat() {
        return _toFormattedString(false, arguments)
    }

    var _toFormattedString = function (l, values) {
        var resultString = "",
            template = values[0];

        for (var i = 0; true;) {
            var startBracket = template.indexOf("{", i),
                endBracket = template.indexOf("}", i);

            if (startBracket < 0 && endBracket < 0) {
                resultString += template.slice(i);
                break
            }
            if (endBracket > 0 && (endBracket < startBracket || startBracket < 0)) {
                resultString += template.slice(i, endBracket + 1);
                i = endBracket + 2;
                continue
            }
            resultString += template.slice(i, startBracket);
            i = startBracket + 1;
            if (template.charAt(i) === "{") {
                resultString += "{";
                i++;
                continue
            }
            if (endBracket < 0)
                break;
            var h = template.substring(i, endBracket),
                g = h.indexOf(":"),
                k = parseInt(g < 0 ? h : h.substring(0, g), 10) + 1,
                a = g < 0 ? "" : h.substring(g + 1),
                b = values[k];

            if (typeof b === "undefined" || b === null)
                b = "";
            if (b.toFormattedString)
                resultString += b.toFormattedString(a);
            else if (l && b.localeFormat)
                resultString += b.localeFormat(a);
            else if (b.format)
                resultString += b.format(a);
            else
                resultString += b.toString();

            i = endBracket + 1
        }
        return resultString
    };

    //endregion StringFormat

    //region deriveProto

    function derivePrototype ( base, sub ) {
        sub.prototype = Object.create(base.prototype);
        sub.prototype.constructor = sub;
    }

    //endregion deriveProto

    //region deriveOverride

    function deriveOverride ( base, sub, overrides ) {
        derivePrototype(base, sub);
        if ( typeof overrides != 'undefined' ) {
            for ( var method in overrides ) {
                sub.prototype[method] = overrides[method];
            }

            if ( overrides.hasOwnProperty('namespace') ) {
                sub.prototype.namespaceHash = getHashString(overrides.namespace);
            }
        }
    }

    //endregion deriveOverride

    //region getHashString

    function getHashString ( str ) {
        var hash = 0, i, chr, len;
        if ( str.length > 0 ) {
            for (i = 0, len = str.length; i < len; i++) {
                chr   = str.charCodeAt(i);
                hash  = ((hash << 5) - hash) + chr;
                hash |= 0;
            }
        }
        return hash.toString();
    }

    //endregion getHashString

    //region getKeys

    function getKeys ( obj ) {
        var res = [];
        for ( var pr in obj ) {
            res.push(pr);
        }
        return res;
    }

    //endregion getKeys

    //region mergeArrays



    //endregion mergeArrays

    //region formatString

    function formatString ( inputString ) {
        var re =  /\{\d+\}/g,
            er, res = inputString;

        for ( var i = 1; i < arguments.length; i++ ) {
            er = re.exec(inputString);
            if ( er !== null ) {
                res = res.replace(er[0], arguments[i]);
            }
        }
        return res;
    }

    //endregion formatString

    //region formatError

    function formatError ( errorName ) {
        var argArr, res;
        if ( COSA.Const.Error.hasOwnProperty(errorName) ) {
            argArr = [COSA.Const.Error[errorName]];
            for ( var i = 1, len = arguments.length; i < len; i++ ) {
                argArr.push(arguments[i]);
            }
            res = formatString.apply(this, argArr);
        }
        return res;
    }

    //endregion formatError

    exports.StringBuilder = StringBuilder;
    exports.StrBuilder = StrBuilder;
    exports.StringFormat = StringFormat;
    exports.derivePrototype = derivePrototype;
    exports.deriveOverride = deriveOverride;
    exports.getHashString = getHashString;
    exports.getKeys = getKeys;
    exports.formatString = formatString;
    exports.formatError = formatError;

})(COSA.Util);
